package com.zmxstudy.rbac.filter;

import cn.hutool.core.util.ObjUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zmxstudy.rbac.config.SecurityConfig;
import com.zmxstudy.rbac.constant.SecurityConstant;
import com.zmxstudy.rbac.util.JwtUtil;
import com.zmxstudy.rbac.util.RedisUtil;
import com.zmxstudy.rbac.util.ResponseUtil;
import com.zmxstudy.rbac.vo.Result;
import com.zmxstudy.rbac.vo.ResultCode;
import io.jsonwebtoken.Claims;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.io.IOException;
import java.util.Arrays;

/**
 * Jwt 权限认证过滤器
 *
 * @author star
 */
@Component
@AllArgsConstructor
public class SecurityJwtAuthenticationFilter implements Filter {
    private JwtUtil jwtUtil;
    private ObjectMapper objectMapper;
    private RedisUtil redisUtil;
    private AntPathMatcher antPathMatcher;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 1、表示已经认证过
        if (ObjUtil.isNotEmpty(SecurityContextHolder.getContext().getAuthentication())) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 2、如果是白名单页则跳过
        if (Arrays.stream(SecurityConfig.WHITE_LIST).anyMatch(white -> antPathMatcher.match(white, request.getRequestURI()))) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 3、获取请求头中的jwt
        String jwtToken = request.getHeader(SecurityConstant.TOKEN_HEADER);
        if (ObjUtil.isEmpty(jwtToken)) {
            ResponseUtil.writeJson(response, objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0305)));
            return;
        }

        // 4、解析 JwtToken
        String username = null;
        try {
            Claims claims = jwtUtil.parse(jwtToken);
            username = claims.getSubject();
        } catch (Exception e) {
            ResponseUtil.writeJson(response, objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0304)));
            return;
        }

        // 5、从 Redis 中取出 authentication
        Authentication authentication = (Authentication) redisUtil.hashGet(SecurityConstant.TOKEN_KEY, username);
        if (ObjUtil.isEmpty(authentication)) {
            ResponseUtil.writeJson(response, objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0311)));
            return;
        }

        // authentication 有效，将其交给 Security，相当于认证成功
        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(request, response);
    }
}
